﻿using System;
using System.Collections.Generic;
using System.ServiceModel;
using ClimateControl.Infrastructure;
using System.Linq;
using ClimateControl.Infrastructure.Extensions;
using System.Timers;
using System.Threading;
using System.Reflection;
using System.Collections.ObjectModel;

namespace ClimateControl.Infrastructure.Service.Server
{
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]
    public class ClimateService : IClimateService, IDisposable
    {
        #region Fields

        private ClimateInfo climateInfo;
        private readonly object thisLock;
        private CallbackManager callbackManager;
        private System.Timers.Timer voteExpiryTimer;
      
        #endregion Fields

        #region Constructor

        public ClimateService()
        {
            this.climateInfo = new ClimateInfo();
            this.thisLock = new object();
            this.callbackManager = new CallbackManager();

            this.voteExpiryTimer = new System.Timers.Timer();
            this.voteExpiryTimer.AutoReset = true;
            this.voteExpiryTimer.Interval = 1000 * 60;
            this.voteExpiryTimer.Elapsed += voteExpiryTimer_Elapsed;
            this.voteExpiryTimer.Enabled = true;
            this.voteExpiryTimer.Start();
        }

        #endregion Constructor

        #region Service Methods

        public void Subscribe()
        {
            lock (thisLock)
            {
                IClimateInfo callback = OperationContext.Current.GetCallbackChannel<IClimateInfo>();
                this.callbackManager.AddCallback(callback);
            }
        }

        public void Vote(VoteOption vote, string user)
        {
            lock (thisLock)
            {
                Vote lastVote = this.climateInfo.Votes.OrderByDescending(v => v.Time).FirstOrDefault(uv => uv.User == user);
                if (lastVote == null || !lastVote.Time.IsInTheLast(this.climateInfo.VoteFrequency).Minutes)
                    this.climateInfo.AddVote(new Vote() { User = user, Time = DateTime.Now, VoteOption = vote });

                this.callbackManager.InvokeVotesChangedCallbacks(this.climateInfo.Votes);
            }
        }

        public void SetIsAirConOn(bool isOn)
        {
            lock (thisLock)
            {
                this.climateInfo.IsAirConOn = isOn;
                this.callbackManager.InvokeIsAirConOnChangedCallbacks(climateInfo.IsAirConOn);
            }
        }

        public void SetTemperature(int temp)
        {
            lock (thisLock)
            {
                this.climateInfo.Temperature = temp;
                this.callbackManager.InvokeTemperatureChangedCallbacks(climateInfo.Temperature);
            }
        }

        public void SetVoteFrequency(double voteFreq)
        {
            lock (thisLock)
            {
                this.climateInfo.VoteFrequency = voteFreq;
                this.callbackManager.InvokeVoteFrequencyChangedCallbacks(climateInfo.VoteFrequency);
            }
        }

        public void SetVoteExpiry(int voteExp)
        {
            lock (thisLock)
            {
                this.climateInfo.VoteExpiry = voteExp;
                this.callbackManager.InvokeVoteExpiryChangedCallbacks(this.climateInfo.VoteExpiry);
                this.callbackManager.InvokeVotesChangedCallbacks(this.climateInfo.Votes);
            }
        }

        public void SetIsBigFanOn(bool isBigFanOn)
        {
            lock (thisLock)
            {
                this.climateInfo.IsBigFanOn = isBigFanOn;
                this.callbackManager.InvokeIsBigFanOnChangedCallbacks(isBigFanOn);
            }
        }

        public void SetUpdateFrequency(int updateFrequency)
        {
            lock (thisLock)
            {
                this.climateInfo.UpdateFrequency = updateFrequency;
                this.callbackManager.InvokeUpdateFrequencyChangedCallbacks(updateFrequency);
            }
        }

        public void ResetVotes()
        {
            lock (thisLock)
            {
                this.climateInfo.Votes = new List<Vote>();
                this.callbackManager.InvokeVotesChangedCallbacks(this.climateInfo.Votes);
            }
        }

        public ClimateInfo GetClimateInfo()
        {
            lock (thisLock)
            {
                this.callbackManager.InvokeVotesChangedCallbacks(this.climateInfo.Votes);
                return this.climateInfo;
            }
        }

        #endregion Service Methods

        #region Events

        void voteExpiryTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            this.callbackManager.InvokeVotesChangedCallbacks(this.climateInfo.Votes);
        }

        #endregion Events

        #region IDisposable

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (voteExpiryTimer != null)
                {
                    voteExpiryTimer.Dispose();
                    voteExpiryTimer = null;
                }
            }
        }

        #endregion IDisposable
    }
}
